/**
 *  \file I3OverlayNetwork.h
 *  \brief Declares the various fields and functions of the i3 OCD.
 */

#ifndef I3_OVERLAY_NETWORK_H
#define I3_OVERLAY_NETWORK_H 1

#include "OCDExport.h"
#include "OCD_include.h"


#include "I3OCDStateInfo.h"

#include "I3Packet.h"

#include <vector>
#include <list>
#include <map>

extern "C" {
    #include "i3.h"
    #include "i3_client_api.h"
    #include "i3_config.h"
}


using namespace std;


class I3Server;
class PublicTrigger;
class I3AddressBook;
class I3Trigger;
class I3OCDStateInfo;
class I3ClientContext;
class OCI;

#define I3_COMPLETED_INIT 9000
#define I3_FAILED_PUBLIC_TRIGGER_INSERT 9001
#define I3_INIT_IN_PROGRESS 9002

/**
  * This class encapsulates the I3OverlayNetwork to the proxy.
  * It is a subclass of OCD
  */
class OCD_WINDOWS_API I3OverlayNetwork : public OCD {
private:

    /** Map of i3 ids to names */
    map<string, string> id_name_map;

    /** Map of last id in a stack to the list of comma separated names corresponding to
      * the previous ids in the stack.
      */
    map<string, string> idStack_names_map;
    
    char closestI3Server [1000];
	ID firstHopId;
	/** This flag is set to true if the id of the first hop server is explicitly specified. */
	bool firstHopIdFlag;

    /** 
      * This is a temporary character array needed while printing out stuff to screen.
      */
    char tmpBuf[MAX_PRINT_BUF];

    /** The internal state of the I3 proxy.
      * For eg: I3_COMPLETED_INIT, I3_FAILED_INIT, I3_INIT_IN_PROGRESS
      */
    unsigned internalState;
   
    /** The UDP port to be used by the proxy. */
    unsigned short i3ProxyPort;

    /** Should we allow i3 ids to be derived by hashing the names? */
    unsigned short enableHashedURLs;

    /** The base anycast trigger from which private trigger ids are assigned
     * when fast connection setup is enabled.
     */
    I3Trigger* baseAnycastTrigger;

    /** This is to keep track of the next private trigger to be allocated
     * from the range under baseAnycastTrigger.
     * This option is used only when fast connection setup is enabled.
     */
    int nextAnycastIndex;
    
    struct sockaddr_in localSockAddr; /* local address, used to receive i3 traffic 
                 * (in network format)
                 */
  
    /** TODO */
    i3_addr           localI3Address;
    
    /** List of public triggers inserted by the proxy into this i3 overlay
    *  network.
    */
    list<PublicTrigger *> publicTriggerList;

    /**
      * Hashtable of I3Trigger objects keyed by their i3ID
      */
    map<string, I3Trigger*> i3TriggerMap;

    /**
      * Hashtable of I3OCDStateInfos keyed by the (private??) id of the
      * remote server (more specifically - the id of the I3OCDStateInfo at the 
      * remote end) the node is masquerading as.  This table can be looked up
      * to retrieve an existing VN for a particular remote id, it exists.
      */
    map<string, I3OCDStateInfo*> remoteVNMap;
    
    /**
      * Hashtable of I3OCDStateInfos keyed by the (private) id of the
      * local end of the connection.  This table can be looked up
      * to retrieve the VN when a packet for a particular id is received.
      */
    map<string, I3OCDStateInfo*> localVNMap;


    /** 
      * Hash table of I3OCDStates which have pending private trigger requests.
      * The hash table is keyed by the id to which the private trigger request
      * was made.
      */
    map<string, I3OCDStateInfo*> privTrigReqMap;
    
    I3ClientContext* context;

    /** AddressBook containing DNS name, i3 id mappings */
    I3AddressBook *i3AddressBook;
    
    /** This flag is true if the TCP fd of i3 is added to the set of fds
      * listened to by the proxy.
      */
    bool addedTCPFDFlag;
 
    /** If this flag is true, the connection establishment private trigger negotiation delay
      * is avoided by initially sending data on the public trigger.
      */
    bool enableFastConnectionStartup;
   
    bool enableShortcut;

    void initConfigValues();
    void initAnycastTriggers ();
    void insertTriggers();
    
    void getNewLocalAnycastTriggerId (ID *id);
    
    void requestPrivateTrigger(I3OCDStateInfo *vn);
    void insertPublicTrigger(PublicTrigger *trig);
    void insertPrivateTrigger(I3Trigger *trig);
    void insertTrigger(I3Trigger *trig, uint8_t insertFlags);

// Trigger Events - Start 
    void processTriggerNotFound(ID *trigId);
    void receivedTriggerEvent( I3Trigger*, cbk_packet*);
    void insertTriggerFailed(I3Trigger* trig);
    void insertedTrigger(I3Trigger *, cbk_packet*);
    
    I3OCDStateInfo* handlePrivateTriggerRequest(
                ID* myId, ID* senderPrivId, 
                bool enableFastConnectionSetup, 
                I3Block *blk,
                const cbk_packet*);

    I3OCDStateInfo* handlePrivateTriggerConfirmation(ID* myId, const I3Block *blk);

// Trigger Events - End
    
    void initFakeState(I3OCDStateInfo *,int, PublicTrigger* assocPubTrig);
    
    void addressChangeCheck();
    void i3ServerUpdate();
    void addAddressChangeCheckTimer();
    void addServerUpdateTimer();

    
    I3Trigger* lookupTrigger(ID);
    I3OCDStateInfo* lookupRemoteVN(ID);
    I3OCDStateInfo* lookupLocalVN(ID);

    void addTrigger(I3Trigger *);
    void addRemoteVN(I3OCDStateInfo* vn);
    void addLocalVN(I3OCDStateInfo* vn);
            
    void removeTrigger(I3Trigger *);
    void removeRemoteVN(I3OCDStateInfo* vn); 
    void removeLocalVN(I3OCDStateInfo* vn); 

    void addPrivTrigReqOCDState (I3OCDStateInfo* );
    void delPrivTrigReqOCDState (ID);
    I3OCDStateInfo * lookupPrivTrigReqMap (ID);

    void destroyVN(I3OCDStateInfo *);

    void packPacket (cl_buf *clb, char* pktContents, int pktSize, I3OCDStateInfo *i3VN);
    cl_buf* packPrivateTriggerConfirmation( I3OCDStateInfo *vn, ID  *id);
    cl_buf* packPrivateTriggerRequest(I3OCDStateInfo *vn, ID* id);
    static  void setI3Block (I3Block *i3Blk, uint8_t i3MsgType, uint16_t blkLen);

#ifdef _WIN32
	TimerEvent* updateStatusTimer;
#endif

public:
    
    I3OverlayNetwork(string type, string id, 
                    string desc, string fileName, 
                    string urlSuffixes, bool handleLegacyURLs
                );
    I3OverlayNetwork(OCD *);

	~I3OverlayNetwork();

#ifdef _WIN32
    const char *getWinRegistryKey();
	void updateStatus();
	void scheduleUpdateStatus();

	void sleepProxy();
#endif

/* Overlay Interface to the proxy - START */
    virtual void init(Proxy*, OCI*, bool recoverFromCrash );

    int getStatus();

    char* toString(char *tmpStr);

    void addNameById (ID *, string);
    string getNameById (ID *);
 
    void addMiddleBoxNamesById (string, string);
    string getMiddleBoxNamesById (ID *);

    void recoverRecord (char *);
    void initMyNames ();
/* Overlay Interface to the proxy - END */


/* Layer Interface - Start */
    void send ( LayerHandle handle, char* data, int dataLen);
    LayerHandle setup (
                const string endPointName, 
                int endPointNameType,
                char *optionBlocks, 
                int optionLen,
                LayerHandle ociHandle,
                OCI* oci );

    string getNextHopId (LayerHandle handle);

    string getMiddleBoxName (LayerHandle handle);
/* Layer Interface - End */

    friend void dummy_insertedTrigger(cl_trigger*, void*, void*);
    friend void dummy_triggerNotFound(void *ctx_data, void *data, void* fun_ctx);
    friend void dummy_constraint_failed(cl_trigger*, void*, void*);
    friend void dummy_receive_packet(cl_trigger*, void*, void*);
    friend void dummy_trigger_refresh_failed(cl_trigger*, void*, void*);

    friend void dummy_add_ack_timeout(cl_trigger *t, void *data, void* fun_ctx);
    friend void dummy_add_refresh_timeout(cl_trigger *t, void *data, void* fun_ctx);

    friend class I3OCDStateInfo;
    friend class I3ClientContext;
    friend class I3Trigger;    

    //HACK:
    friend class Proxy;
};

#endif
